package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.article.service.HotArticleService;
import com.heima.commom.exception.CustException;
import com.heima.common.constants.article.HotArticleConstants;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.model.mess.app.NewBehaviorDTO;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.support.collections.DefaultRedisList;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author by jojo
 * @Date 2022/3/13
 * @Description
 */
@Component
@Slf4j
public class UpdateHotArticleJob {

    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    HotArticleService hotArticleService;
    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String param){
        log.info("热点文章分值开始更新，调度任务开始执行......");
        // 1. 获取redis 行为列表中待处理数据
        List<NewBehaviorDTO> behaviorList= getRedisBehaviorList();
        if (CollectionUtils.isEmpty(behaviorList)) {
            log.info("热点文章分值更新，10s未产生任何文章行为....");
            return ReturnT.SUCCESS;
        }
        // 2. 将数据按照文章分组  进行聚合统计 得到待更新的数据列表
        List<AggBehaviorDTO> aggBehaviorList=getAggBehaviorList(behaviorList);
        if (CollectionUtils.isEmpty(aggBehaviorList)) {
            log.info("热点文章分值更新，10s未产生任何文章行为....");
            return ReturnT.SUCCESS;
        }
        // 3. 定时更新更新数据库文章分值
        aggBehaviorList.forEach(hotArticleService::updateApArticle);
        log.info("热点文章分值更新完毕，调度任务完成......");
        return ReturnT.SUCCESS;
    }

    /**
     * 分组聚合处理文章的行为（点赞，阅读，收藏，关注）
     * @param newBehaviorList
     * @return
     */
    private List<AggBehaviorDTO> getAggBehaviorList(List<NewBehaviorDTO> newBehaviorList) {
        // 1. 按照文章id进行分组   map<文章id, 该文章所有行为集合>
        ArrayList<AggBehaviorDTO> dtoList = new ArrayList<>();
        Map<Long, List<NewBehaviorDTO>> listMap = newBehaviorList.stream()
                .collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));
        // 2. 遍历  map<文章id, 该文章所有行为集合>
        listMap.forEach((articleId,newBehaviors)->{
            //  该文章所有行为集合.stream()
            //                  .map  将每个行为 都转为 aggBehavior聚合对象
            Optional<AggBehaviorDTO> reduceResult = newBehaviors.stream().map(this::parseAggBehavior)
                    //归并 将当前文章的所有聚合对象 合并到一起，比如1 + 2之后再加上3...
                    .reduce((k1, k2) -> {
                        k1.setView(k1.getView() + k2.getView());
                        k1.setLike(k1.getLike() + k2.getLike());
                        k1.setComment(k1.getComment() + k2.getComment());
                        k1.setCollect(k1.getCollect() + k2.getCollect());
                        return k1;
                    });
            if (reduceResult.isPresent()){
                dtoList.add(reduceResult.get());
            }
        });
        return dtoList;
    }

    private AggBehaviorDTO parseAggBehavior(NewBehaviorDTO behavior) {
        AggBehaviorDTO dto = new AggBehaviorDTO();
        dto.setArticleId(behavior.getArticleId());
        switch (behavior.getType()){
            case LIKES:
                dto.setLike(behavior.getAdd());
                break;
            case VIEWS:
                dto.setView(behavior.getAdd());
                break;
            case COMMENT:
                dto.setComment(behavior.getAdd());
                break;
            case COLLECTION:
                dto.setCollect(behavior.getAdd());
                break;
            default:
        }
        return dto;
    }


    /**
     * 获取redis 行为列表中待处理数据
     * @return
     */
    private List<NewBehaviorDTO> getRedisBehaviorList() {
        // 1. 准备lua脚本   ( llen 查询队列长度   lrange  查询指定数据  ltrim 截取保留数据)
        // 2. 执行lua脚本命令
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        // 设置脚本返回值类型
        redisScript.setResultType(List.class);
        // 设置脚本
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis.lua")));
        //获取Redis缓存数据
        List<String> result = redisTemplate.execute(redisScript, Arrays.asList(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST));
        return result.stream()
                .map(jsonStr-> JSON.parseObject(jsonStr,NewBehaviorDTO.class))
                .collect(Collectors.toList());
    }
}
